package com.means.polymerize.assembly.aspect.page;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.pagehelper.PageException;
import com.github.pagehelper.PageHelper;
import com.means.polymerize.common.constants.Global;
import com.means.polymerize.common.lang.DataResult;
import com.means.polymerize.common.lang.TableDataInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * The type Page aspect.
 *
 * @author xuhaifeng
 * @since 2021.10.30
 */
@Aspect
@Component
public class PageAspect {

    /**
     * Page point cut.
     */
    @Pointcut("@annotation(com.means.polymerize.assembly.aspect.page.EnablePage)")
    public void pagePointCut() {
    }

    /**
     * Page point cut table data info.
     *
     * @param joinPoint the join point
     * @return the table data info
     * @throws Throwable the throwable
     */
    @Around("pagePointCut()")
    public Object pagePointCut(ProceedingJoinPoint joinPoint) throws Throwable {
        final Object proceed = joinPoint.proceed();
        final AtomicReference<Object> rspData = new AtomicReference<>();
        rspData.set(proceed);
        if (proceed instanceof TableDataInfo) {
            final EnablePage annotation = ((MethodSignature)
                    joinPoint.getSignature()).getMethod().getAnnotation(EnablePage.class);
            final PageDomain pageDomain = TableSupport.buildPageRequest();
            final AtomicReference<Integer> pageNum = new AtomicReference<>(pageDomain.getPageNum());
            final AtomicReference<Integer> pageSize = new AtomicReference<>(pageDomain.getPageSize());
            if (Objects.nonNull(pageNum.get())) {
                pageNum.set(annotation.pageNum());
            }
            if (Objects.nonNull(pageSize.get())) {
                pageSize.set(annotation.pageSize());
            }
            if (pageSize.get() > Global.NUM_I_50) {
                throw new PageException();
            } else {
                if (StringUtils.isBlank(pageDomain.getOrderByColumn())) {
                    pageDomain.setOrderByColumn(annotation.orderByColumn());
                }
                if (Objects.nonNull(pageDomain.getOrderByMode())) {
                    pageDomain.setOrderByMode(annotation.orderByMode());
                }
                rspData.set(DataResult.success(
                        TableDataInfo.create(((TableDataInfo<?>) proceed).getRows(),
                                PageHelper.startPage(pageNum.get(), pageSize.get(), SqlUtil.escapeOrderBySql(pageDomain.getOrderBy())).getTotal())
                ));
            }
        }
        return rspData;
    }

    private static class SqlUtil {
        /**
         * The constant SQL_PATTERN.
         */
        public static String SQL_PATTERN = "[a-zA-Z0-9_ ,.]+";

        /**
         * Escape order by sql string.
         *
         * @param value the value
         * @return the string
         */
        public static String escapeOrderBySql(String value) {
            if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
                throw new RuntimeException("参数不符合规范，不能进行查询");
            } else {
                return value;
            }
        }

        /**
         * Is valid order by sql boolean.
         *
         * @param value the value
         * @return the boolean
         */
        public static boolean isValidOrderBySql(String value) {
            return value.matches(SQL_PATTERN);
        }
    }
}
